#ifndef ALLOCATOR_H
#define ALLOCATOR_H

#include "defines.h"
#include <assert.h>

struct Chuck
{
    /// data pointer which points to the unallocated space
    uchar8* pData_;
    /// holds the index of the first block available in this chunk
    uchar8 firstAvailableBlock_;
    /// the number of blocks available in this chunk
    uchar8 blocksAvailable_;
    Chuck(): pData_(NULL), firstAvailableBlock_(0), blocksAvailable_(0){}
    /// Initialize the chuck
    /// use U8(1 byte) as the smallest element for alignment
    /// @param blockSize the block size of each block in this chunk
    /// @param blocks the number of the blocks in this chuck
    void Init(uint32 blockSize, uchar8 blocks)
    {
        pData_ = new uchar8[blockSize * blocks];
        firstAvailableBlock_ = 0;
        blocksAvailable_ = blocks;
        unsigned char i = 0;
        unsigned char* p = pData_;
        // Save index to first char of each block 
        for (;  i!= blocks; p += blockSize) {
            *p = ++i;
        }
    }
    /// Allocate one block
    void* Allocate(uint32 blockSize)
    {
        if (!blocksAvailable_) {
            return 0;
        }
        U8* pResult = pData_ + (firstAvailableBlock_ * blockSize);
        // Update first available block to point to the next block
        firstAvailableBlock_ = *pResult;
        --blocksAvailable_;
        return pResult;
    }
    /// Deallocate one block
    void Deallocator(void *p, uint32 blockSize)
    {
        assert(p >= pData_);
        U8* toRelease = static_cast<U8*>(p);
        // Alignment check
        assert((toRelease-pData_)% blockSize == 0);
        *toRelease = firstAvailableBlock_;
        firstAvailableBlock_ = static_cast<U8>((toRelease-pData_) / blockSize);
        // Truncation check
        assert(firstAvailableBlock_ == (toRelease - pData_) / blockSize);
        ++blocksAvailable_;
    }
    void fini() {
        delete [] pData_;
    }
};

#endif